

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=dark>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/my-blog/img/fluid.png">
  <link rel="icon" href="/my-blog/img/fluid.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="木舟">
  <meta name="keywords" content="python Vue Test 测试开发 自动化测试">
  
    <meta name="description" content="前言  装饰器是Python进阶中比较重要的一个知识点，也是在测试开发面试中高频出现的面试题目。  掌握装饰器，能够使你编写的代码更加简洁，能够让你低成本的为函数扩展新的功能。  大部分初学者可能已经接触到装饰器的知识点，但是却不知道该如何去使用，或者说不知道在哪些地方使用。  本文将会带领读者，逐步掌握装饰器，过程中也会对涉及到的其他知识点做一定的扩充。 闭包  嗯？我们不是学习装饰器嘛？为什么">
<meta property="og:type" content="article">
<meta property="og:title" content="Decorators(装饰器 -- 让你的代码更python范儿)">
<meta property="og:url" content="https://walker_kwu.gitee.io/my-blog/2022/07/13/python/advanced/decorators/">
<meta property="og:site_name" content="My Blog">
<meta property="og:description" content="前言  装饰器是Python进阶中比较重要的一个知识点，也是在测试开发面试中高频出现的面试题目。  掌握装饰器，能够使你编写的代码更加简洁，能够让你低成本的为函数扩展新的功能。  大部分初学者可能已经接触到装饰器的知识点，但是却不知道该如何去使用，或者说不知道在哪些地方使用。  本文将会带领读者，逐步掌握装饰器，过程中也会对涉及到的其他知识点做一定的扩充。 闭包  嗯？我们不是学习装饰器嘛？为什么">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2022-07-13T10:37:52.000Z">
<meta property="article:modified_time" content="2023-05-23T10:37:53.263Z">
<meta property="article:author" content="木舟">
<meta property="article:tag" content="测试开发">
<meta property="article:tag" content="Python进阶">
<meta name="twitter:card" content="summary_large_image">
  
  
    <meta name="referrer" content="no-referrer-when-downgrade">
  
  
  <title>Decorators(装饰器 -- 让你的代码更python范儿) - My Blog</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/my-blog/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/my-blog/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/my-blog/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"walker_kwu.gitee.io","root":"/my-blog/","version":"1.9.2","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"left","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/my-blog/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/my-blog/js/utils.js" ></script>
  <script  src="/my-blog/js/color-schema.js" ></script>
  


  
<meta name="generator" content="Hexo 6.2.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/my-blog/">
      <strong>木舟</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/my-blog/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="Decorators(装饰器 -- 让你的代码更python范儿)"></span>
          
        </div>

        
          
  <div class="mt-3">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-author" aria-hidden="true"></i>
        木舟
      </span>
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2022-07-13 18:37" pubdate>
          2022年7月13日 晚上
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          6.6k 字
        
      </span>
    

    

    
    
      
        <span id="busuanzi_container_page_pv" style="display: none">
          <i class="iconfont icon-eye" aria-hidden="true"></i>
          <span id="busuanzi_value_page_pv"></span> 次
        </span>
        
      
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="padding-left: 2rem; margin-right: -1rem">
    <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">Decorators(装饰器 -- 让你的代码更python范儿)</h1>
            
              <p class="note note-info">
                
                  
                    本文最后更新于：2023年5月23日 晚上
                  
                
              </p>
            
            
              <div class="markdown-body">
                
                <h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>  <em><strong>装饰器</strong></em>是Python进阶中比较重要的一个知识点，也是在<strong>测试开发</strong>面试中高频出现的面试题目。<br>  掌握<em><strong>装饰器</strong></em>，能够使你编写的代码更加简洁，能够让你低成本的为函数扩展新的功能。<br>  大部分初学者可能已经接触到<em><strong>装饰器</strong></em>的知识点，但是却不知道该如何去使用，或者说不知道在哪些地方使用。<br>  本文将会带领读者，逐步掌握<em><strong>装饰器</strong></em>，过程中也会对涉及到的<strong>其他</strong>知识点做一定的扩充。</p>
<h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p>  嗯？我们不是学习装饰器嘛？为什么会跳出一个<strong>闭包</strong>，你可能会感觉到有点陌生。<br>  但是，这是学习装饰器的前提，就是要了解<strong>闭包</strong>的概念。<br>  因为装饰器的本质上就是一个<strong>闭包</strong>。</p>
<h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><ul>
<li>外部函数中定义一个内部函数</li>
<li>内部函数引用外部函数中的变量</li>
<li>外部函数的返回值是内部函数</li>
</ul>
<h3 id="语法举例"><a href="#语法举例" class="headerlink" title="语法举例"></a>语法举例</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">f1</span>():<br>    n=<span class="hljs-number">999</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">f2</span>():<br>        <span class="hljs-built_in">print</span>(n)<br>    <span class="hljs-keyword">return</span> f2<br>i = f1()  <span class="hljs-comment"># 这个时候，创建了内部变量n=999, 这时候 i 实际上为 f2</span><br><br>i() <span class="hljs-comment"># 这个时候，相当于执行了f2()</span><br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-comment"># 999</span><br><span class="hljs-comment"># 999</span><br></code></pre></td></tr></table></figure>
<p>  可以发现，<strong>不论</strong>我执行了多少次 <code>i()</code>, 都会打印 <code>999</code>。这也是闭包的一个<strong>特性</strong>：<br>  <em><strong>外函数创建的私有变量，会一直保存在内存中，可以一直调用</strong></em><br>  我们可以通过下面的例子，来证实这一观点。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">f1</span>():<br>    n = <span class="hljs-number">0</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">f2</span>():<br>        <span class="hljs-keyword">nonlocal</span> n  <span class="hljs-comment"># 注意这里</span><br>        n += <span class="hljs-number">1</span><br>        <span class="hljs-built_in">print</span>(n)<br>    <span class="hljs-keyword">return</span> f2<br><br>i = f1()<br><br>i()<br>i()<br>i()<br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-comment"># 1</span><br><span class="hljs-comment"># 2</span><br><span class="hljs-comment"># 3</span><br></code></pre></td></tr></table></figure>
<p>  可以发现，执行了三遍 <code>i()</code>, 变量 <code>n</code>，从<strong>0</strong>随着执行次数变成了最终的<strong>3</strong>。<br>  细心的读者已经发现了，内函数中加了关键字 <code>nonlocal</code>, 该关键字的作用是内部函数调用外部函数的变量。<br>  如果我们把它去掉：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">f1</span>():<br>    n = <span class="hljs-number">0</span><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">f2</span>():<br>        n += <span class="hljs-number">1</span><br>        <span class="hljs-built_in">print</span>(n)<br>    <span class="hljs-keyword">return</span> f2<br><br>i = f1()<br>i()<br><br><span class="hljs-comment"># 运行结果: 报错，变量n没有被定义</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">Traceback (most recent call last):</span><br><span class="hljs-string">  File &quot;C:/Users/sasa/Desktop/test/update.py&quot;, line 9, in &lt;module&gt;</span><br><span class="hljs-string">    i()</span><br><span class="hljs-string">  File &quot;C:/Users/sasa/Desktop/test/update.py&quot;, line 4, in f2</span><br><span class="hljs-string">    n += 1</span><br><span class="hljs-string">UnboundLocalError: local variable &#x27;n&#x27; referenced before assignment</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>

<p>为什么会这样，这里就可以扩充一个知识点：<strong>作用域</strong>。<br>待会我们再来讲这个知识点</p>
<h3 id="使用闭包的注意点："><a href="#使用闭包的注意点：" class="headerlink" title="使用闭包的注意点："></a>使用闭包的注意点：</h3><ul>
<li>内存消耗(因为外函数变量无法被回收)，所以使用闭包要注意：不使用的变量要手动删除</li>
<li>闭包无法改变外部函数局部变量指向的内存地址(可以看看接下来的<strong>作用域</strong>)</li>
<li>返回闭包时，返回函数不要引用任何循环变量，或者后续会发生变化的变量</li>
</ul>
<p>  关于第三点，可以看下面这个例子(摘抄自互联网)：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">count</span>():<br>    fs = []<br>    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">4</span>):<br>        <span class="hljs-keyword">def</span> <span class="hljs-title function_">f</span>():<br>             <span class="hljs-keyword">return</span> i*i<br>        fs.append(f)<br>    <span class="hljs-keyword">return</span> fs<br><br>f1, f2, f3 = count()<br>f1()<br>f2()<br>f3()<br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">9</span><br><span class="hljs-string">9</span><br><span class="hljs-string">9</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>
<p>在上面的例子中，每次循环，都创建了一个新的函数，然后，把创建的3个函数都放在列表中，通过列表整体返回了。</p>
<p>你可能认为调用<code>f1()，f2()和f3()</code>结果应该是<code>1，4，9</code>，但实际结果让你大跌眼镜。<br>因为在向列表中添加 <strong>func</strong> 的时候，<code>i</code> 的值没有固定到f的实例对象中，而仅是将计算公式固定到了实例对象中。<br>等到了调用<code>f1()、f2()、f3()</code>的时候才去取<code>i</code>的值，这时候循环已经结束，<code>i</code> 的值是<code>3</code>，所以结果都是<code>9</code>。</p>
<h2 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h2><p>  编程语言一般把<strong>作用域</strong>分为：</p>
<ul>
<li>全局作用域</li>
<li>局部作用域</li>
</ul>
<p>  在上述例子中，<code>f1()</code>声明的变量，作用域在<strong>定义</strong>它的函数中。<br>  我们可以在<code>f2()</code>中进行变量正常<strong>引用</strong>，但是<code>n+=1</code>，会进行在<strong>作用域</strong>内<strong>声明</strong>的变量的<strong>重新赋值</strong>操作，会涉及<em><strong>直接修改内存地址</strong></em>.<br>  如果我们没有声明<code>nonlocal</code>, 直接去执行<code>n += 1</code>，在<code>f2</code>的作用域内是找不到变量<code>n</code>的内存地址的。<br>  我们可以看下面这三个例子：  </p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># id()可以查看变量的内存地址</span><br><span class="hljs-comment"># 例子一：nonlocal</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">f1</span>():<br>    n = <span class="hljs-number">0</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;最开始n的内存地址：<span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">f2</span>():<br>        <span class="hljs-keyword">nonlocal</span> n<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;开始修改前n的内存地址: <span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>        n += <span class="hljs-number">1</span><br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;此时的n: <span class="hljs-subst">&#123;n&#125;</span>&quot;</span>)<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;此时的n的内存地址: <span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>    <span class="hljs-keyword">return</span> f2<br><br>i = f1()<br>i()<br>i()<br>i()<br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">最开始n的内存地址：140730425008944</span><br><span class="hljs-string">开始修改前n的内存地址: 140730425008944</span><br><span class="hljs-string">此时的n: 1</span><br><span class="hljs-string">此时的n的内存地址: 140730425008976</span><br><span class="hljs-string">开始修改前n的内存地址: 140730425008976</span><br><span class="hljs-string">此时的n: 2</span><br><span class="hljs-string">此时的n的内存地址: 140730425009008</span><br><span class="hljs-string">开始修改前n的内存地址: 140730425009008</span><br><span class="hljs-string">此时的n: 3</span><br><span class="hljs-string">此时的n的内存地址: 140730425009040</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 例子二：重新声明, 值不同</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">f1</span>():<br>    n = <span class="hljs-number">0</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;外函数n的内存地址：<span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">f2</span>():<br>        n = <span class="hljs-number">1</span><br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;内函数n的内存地址: <span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>        n += <span class="hljs-number">1</span><br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;此时的n: <span class="hljs-subst">&#123;n&#125;</span>&quot;</span>)<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;此时的内函数n的内存地址: <span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>    <span class="hljs-keyword">return</span> f2<br><br>i = f1()<br>i()<br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">外函数n的内存地址：140730425008944</span><br><span class="hljs-string">内函数n的内存地址: 140730425008976</span><br><span class="hljs-string">此时的n: 2</span><br><span class="hljs-string">此时的内函数n的内存地址: 140730425009008</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 例子三：重新声明，值相同</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">f1</span>():<br>    n = <span class="hljs-number">0</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;外函数n的内存地址：<span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">f2</span>():<br>        n = <span class="hljs-number">0</span><br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;内函数n的内存地址: <span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>        n += <span class="hljs-number">1</span><br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;此时的n: <span class="hljs-subst">&#123;n&#125;</span>&quot;</span>)<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;此时的内函数n的内存地址: <span class="hljs-subst">&#123;<span class="hljs-built_in">id</span>(n)&#125;</span>&quot;</span>)<br>    <span class="hljs-keyword">return</span> f2<br><br>i = f1()<br>i()<br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">外函数n的内存地址：140730425008944 </span><br><span class="hljs-string">内函数n的内存地址: 140730425008944 # 地址一样哎</span><br><span class="hljs-string">此时的n: 1</span><br><span class="hljs-string">此时的内函数n的内存地址: 140730425008976</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>
<p>通过观察例子三，我们又发现了个小不同。<br>当声明<strong>相同值</strong>时，内存地址不会改变。<br>因为python会有先查找该值已经存在的内存地址，然后改变它的作用域。</p>
<h3 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h3><p>  关于作用域和内存地址，作者其实也是一知半解。<br>  如果讲解的地方不正常或者有疑问，欢迎来进行讨论，联系方式在【关于页】。</p>
<h2 id="装饰器"><a href="#装饰器" class="headerlink" title="装饰器"></a>装饰器</h2><p>  讲完了<strong>闭包</strong>，相信大家会下面的<strong>装饰器</strong>掌握的会更加快。</p>
<h3 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h3><ul>
<li>除了下面两点，其他的和【闭包】一致</li>
<li>将函数当作参数传递进外函数</li>
<li>内函数中调用【函数】参数来运行<h3 id="语法举例-1"><a href="#语法举例-1" class="headerlink" title="语法举例"></a>语法举例</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">outter</span>(<span class="hljs-params">func</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">wrapper</span>():<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;函数：<span class="hljs-subst">&#123;func.__name__&#125;</span>，即将运行&quot;</span>)<br>        func()<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;函数：<span class="hljs-subst">&#123;func.__name__&#125;</span>，运行结束&quot;</span>)<br>    <span class="hljs-keyword">return</span> wrapper<br><br><span class="hljs-meta">@outter</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">func1</span>():<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;我运行啦&quot;</span>)<br><br>func1()<br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">函数：func1，即将运行</span><br><span class="hljs-string">我运行啦</span><br><span class="hljs-string">函数：func1，运行结束</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>
可以发现，<strong>装饰器</strong>函数，就是拿来被装饰的。必须要放在<strong>被装饰</strong>函数上方。<br>就像是头上戴了一朵花，让一个人更美。<br>实际上<code>func1</code>可以拆解成如下：<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">outter</span>(<span class="hljs-params">func</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">wrapper</span>():<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;函数：<span class="hljs-subst">&#123;func.__name__&#125;</span>，即将运行&quot;</span>)<br>        func()<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;函数：<span class="hljs-subst">&#123;func.__name__&#125;</span>，运行结束&quot;</span>)<br>    <span class="hljs-keyword">return</span> wrapper<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">func1</span>():<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;我运行啦&quot;</span>)<br><br>r = outter(func1)<br>r()<br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">函数：func1，即将运行</span><br><span class="hljs-string">我运行啦</span><br><span class="hljs-string">函数：func1，运行结束</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure></li>
</ul>
<h3 id="带参数的装饰器"><a href="#带参数的装饰器" class="headerlink" title="带参数的装饰器"></a>带参数的装饰器</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">outter</span>(<span class="hljs-params">func</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">wrapper</span>(<span class="hljs-params">text</span>):<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;函数：<span class="hljs-subst">&#123;func.__name__&#125;</span>，即将运行&quot;</span>)<br>        func(text)<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;函数：<span class="hljs-subst">&#123;func.__name__&#125;</span>，运行结束&quot;</span>)<br>    <span class="hljs-keyword">return</span> wrapper<br><br><span class="hljs-meta">@outter</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">func1</span>(<span class="hljs-params">text</span>):<br>    <span class="hljs-built_in">print</span>(text)<br><br>func1(<span class="hljs-string">&#x27;我运行了&#x27;</span>)<br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">函数：func1，即将运行</span><br><span class="hljs-string">我运行啦</span><br><span class="hljs-string">函数：func1，运行结束</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>
<p>  有读者可能会有疑问，<strong>装饰器</strong>不是可以公用嘛？<br>  那如果其他函数的参数不止一个或者说不确定怎么办？</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">outter</span>(<span class="hljs-params">func</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">wrapper</span>(<span class="hljs-params">*args, **kwargs</span>):<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;函数：<span class="hljs-subst">&#123;func.__name__&#125;</span>，即将运行&quot;</span>)<br>        func(*args, **kwargs)<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;函数：<span class="hljs-subst">&#123;func.__name__&#125;</span>，运行结束&quot;</span>)<br>    <span class="hljs-keyword">return</span> wrapper<br><br><span class="hljs-meta">@outter</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">func1</span>(<span class="hljs-params">text, text2</span>):<br>    <span class="hljs-built_in">print</span>(text)<br>    <span class="hljs-built_in">print</span>(text2)<br><br>func1(<span class="hljs-string">&#x27;我运行了&#x27;</span>, <span class="hljs-string">&#x27;我运行了2&#x27;</span>)<br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">函数：func1，即将运行</span><br><span class="hljs-string">我运行了</span><br><span class="hljs-string">我运行了2</span><br><span class="hljs-string">函数：func1，运行结束</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>
<p>  可以发现，加上<code>*args, **kwargs</code>，就可以接受<em><strong>任意数量、任意类型</strong></em>的参数了。<br>  如果读者对以上两个关键字有疑惑，可以适当补充一些与<strong>函数</strong>相关的知识点。</p>
<h2 id="装饰器使用场景"><a href="#装饰器使用场景" class="headerlink" title="装饰器使用场景"></a>装饰器使用场景</h2><h3 id="场景一：计算函数运行时间，抓出耗时函数"><a href="#场景一：计算函数运行时间，抓出耗时函数" class="headerlink" title="场景一：计算函数运行时间，抓出耗时函数"></a>场景一：计算函数运行时间，抓出耗时函数</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> time<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">outter</span>(<span class="hljs-params">func</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">wrapper</span>(<span class="hljs-params">*args, **kwargs</span>):<br>        start_time = time.time()<br>        func(*args, **kwargs)<br>        end_time = time.time()<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;运行时间: <span class="hljs-subst">&#123;end_time - start_time&#125;</span>&quot;</span>)<br>    <span class="hljs-keyword">return</span> wrapper<br><br><span class="hljs-meta">@outter</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">func1</span>(<span class="hljs-params">text</span>):<br>    <span class="hljs-built_in">print</span>(text)<br>    time.sleep(<span class="hljs-number">3</span>) <span class="hljs-comment"># 停止三秒</span><br><br>func1(<span class="hljs-string">&#x27;我运行了&#x27;</span>)<br><br><span class="hljs-comment"># 运行结果</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">我运行了</span><br><span class="hljs-string">运行时间: 3.0028092861175537</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>

<h3 id="场景二-打印日志、记录操作"><a href="#场景二-打印日志、记录操作" class="headerlink" title="场景二: 打印日志、记录操作"></a>场景二: 打印日志、记录操作</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">outter</span>(<span class="hljs-params">func</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">wrapper</span>(<span class="hljs-params">*args, **kwargs</span>):<br>        request = kwargs[<span class="hljs-string">&#x27;request&#x27;</span>]<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;当前操作人: <span class="hljs-subst">&#123;request[<span class="hljs-string">&#x27;user&#x27;</span>]&#125;</span>&quot;</span>)<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;输入参数: <span class="hljs-subst">&#123;request[<span class="hljs-string">&#x27;body&#x27;</span>]&#125;</span>&quot;</span>)<br>        sql = <span class="hljs-string">&quot;insert into xxx&quot;</span>  <span class="hljs-comment"># 这里可以进行数据记录</span><br>        func(*args, **kwargs)<br>    <span class="hljs-keyword">return</span> wrapper<br><br><span class="hljs-meta">@outter</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">func1</span>(<span class="hljs-params">request</span>):<br>    ...<br><br>request_data = &#123;<br>    <span class="hljs-string">&#x27;user&#x27;</span>: <span class="hljs-string">&#x27;木舟&#x27;</span>,<br>    <span class="hljs-string">&#x27;body&#x27;</span>: &#123;<br>        <span class="hljs-string">&#x27;num&#x27;</span>: <span class="hljs-number">1</span><br>    &#125;<br>&#125;<br>func1(request=request_data)<br><br><span class="hljs-comment"># 运行结果：</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">当前操作人: 木舟</span><br><span class="hljs-string">输入参数: &#123;&#x27;num&#x27;: 1&#125;</span><br><span class="hljs-string">&quot;&quot;&quot;</span><br></code></pre></td></tr></table></figure>
<h3 id="场景三：为接口进行token校验"><a href="#场景三：为接口进行token校验" class="headerlink" title="场景三：为接口进行token校验"></a>场景三：为接口进行token校验</h3><p>在<strong>后端开发</strong>中，经常会涉及到<strong>token</strong>校验问题。<br>目前的后端框架都有比较成熟的token框架。<br>但是我们也可以基于<strong>装饰器</strong>，来实现我们自定义的token校验函数。给需要进行校验的接口进行装饰。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 以下只是伪代码</span><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">decrypt_token</span>(<span class="hljs-params">token</span>):<br>    <span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">    token解密</span><br><span class="hljs-string">    :param token:</span><br><span class="hljs-string">    :return:</span><br><span class="hljs-string">    &quot;&quot;&quot;</span><br>    user = <span class="hljs-string">&#x27;木舟&#x27;</span><br>    exp_time = <span class="hljs-string">&#x27;2011-11-11 11:11:11&#x27;</span><br>    <span class="hljs-keyword">return</span> user, exp_time<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">check_token</span>(<span class="hljs-params">func</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">check</span>(<span class="hljs-params">token</span>):<br>        user, exp_time = decrypt_token(token)<br>        now_time = <span class="hljs-string">&#x27;2011-11-11 11:11:11&#x27;</span>  <span class="hljs-comment"># 当前时间</span><br>        <span class="hljs-keyword">if</span> exp_time &lt; now_time: <span class="hljs-comment"># 如果token记录的时间小于当前时间，那么就失效咯</span><br>            <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;token失效&quot;</span><br>        <span class="hljs-keyword">else</span>:<br>            func(token)<br>    <span class="hljs-keyword">return</span> check<br><br><span class="hljs-meta">@check_token</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">api1</span>(<span class="hljs-params">token</span>):<br>    ...<br><br>token = <span class="hljs-string">&#x27;adssadasdasdadasda 随便写的超级加密 token sadiu!@#!@#!ASDASD!@#$@sdkmnalkh&#x27;</span><br>api1(token)<br></code></pre></td></tr></table></figure>

<h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>  以上内容并不是<strong>装饰器</strong>的全部知识点，作者只是挑拣核心概念以及实际使用场景进行编写。<br>  有兴趣的读者，可以看看<strong>带参数的装饰器</strong>、<strong>类装饰器</strong>。<br>  <strong>装饰器</strong>是python重中之重，通过学习<strong>装饰器</strong>，可以让你收获的不仅仅是装饰器本身，更多的是一些编程思想、拓展知识的补充。</p>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/my-blog/categories/python/" class="category-chain-item">python</a>
  
  
    <span>></span>
    
  <a href="/my-blog/categories/python/Python%E8%BF%9B%E9%98%B6/" class="category-chain-item">Python进阶</a>
  
  

  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/my-blog/tags/%E6%B5%8B%E8%AF%95%E5%BC%80%E5%8F%91/">#测试开发</a>
      
        <a href="/my-blog/tags/Python%E8%BF%9B%E9%98%B6/">#Python进阶</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>Decorators(装饰器 -- 让你的代码更python范儿)</div>
      <div>https://walker_kwu.gitee.io/my-blog/2022/07/13/python/advanced/decorators/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>木舟</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2022年7月13日</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>更新于</div>
          <div>2023年5月23日</div>
        </div>
      
      <div class="license-meta-item">
        <div>许可协议</div>
        <div>
          
            
            
              <a target="_blank" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
              <span class="hint--top hint--rounded" aria-label="BY - 署名">
                <i class="iconfont icon-by"></i>
              </span>
              </a>
            
              <a target="_blank" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
              <span class="hint--top hint--rounded" aria-label="NC - 非商业性使用">
                <i class="iconfont icon-nc"></i>
              </span>
              </a>
            
              <a target="_blank" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
              <span class="hint--top hint--rounded" aria-label="SA - 相同方式共享">
                <i class="iconfont icon-sa"></i>
              </span>
              </a>
            
          
        </div>
      </div>
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/my-blog/2022/07/16/python/advanced/with/" title="with -- 优雅永不过时">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">with -- 优雅永不过时</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/my-blog/2022/07/11/top/" title="写在前面">
                        <span class="hidden-mobile">写在前面</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <span>By 木舟</span> <i class="iconfont icon-love"></i> 
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="busuanzi_container_site_pv" style="display: none">
        总访问量 
        <span id="busuanzi_value_site_pv"></span>
         次
      </span>
    
    
      <span id="busuanzi_container_site_uv" style="display: none">
        总访客数 
        <span id="busuanzi_value_site_uv"></span>
         人
      </span>
    
    
  
</div>

  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/my-blog/js/events.js" ></script>
<script  src="/my-blog/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/my-blog/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      headingSelector : CONFIG.toc.headingSelector || 'h1,h2,h3,h4,h5,h6',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      collapseDepth   : CONFIG.toc.collapseDepth || 0,
      scrollSmooth    : true,
      headingsOffset  : -boardTop
    });
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.10/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/my-blog/js/local-search.js" ></script>

  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/my-blog/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
